import { isUndef, isDef } from "./util";
import { STR } from "./";

function patchVnode(oldVnode, vnode) {
  if (oldVnode === vnode) {
    return;
  }

  vnode.renderFn = oldVnode.renderFn;

  if (vnode.tag !== STR) {
    let oldCh = oldVnode.child;
    let ch = vnode.child;
    if (isDef(oldCh) && isDef(ch)) {
      if (oldCh !== ch) {
        reconcile(oldCh, ch);
      }
    } else if (isDef(ch)) {
      // if (process.env.NODE_ENV !== "production") {
      //   checkDuplicateKeys(ch);
      // }
      // if (isDef(oldVnode.text)) {
      //   nodeOps.setTextContent(elm, "");
      // }
      // addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
    } else if (isDef(oldCh)) {
      // removeVnodes(oldCh, 0, oldCh.length - 1);
    }
  }
  return vnode;
}

function sameVnode(oldVnode, newVnode) {
  return (
    oldVnode === newVnode ||
    (oldVnode &&
      newVnode &&
      oldVnode.key === newVnode.key &&
      oldVnode.tag === newVnode.tag)
  );
}

function checkDuplicateKeys(ch) {}

function createKeyToOldIdx(children, beginIdx, endIdx) {
  var i, key;
  var map = {};
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key;
    if (isDef(key)) {
      map[key] = i;
    }
  }
  return map;
}

function findIdxInOld(node, oldCh, start, end) {
  for (var i = start; i < end; i++) {
    var c = oldCh[i];
    if (isDef(c) && sameVnode(node, c)) {
      return i;
    }
  }
}

function reconcile(oldCh, newCh) {
  if (isUndef(oldCh) && isDef(newCh)) {
    return newCh;
  }

  if (isUndef(oldCh) && isUndef(newCh)) {
    return null;
  }

  // use vue reconcile algorithm
  let oldStartIdx = 0;
  let newStartIdx = 0;
  let oldEndIdx = oldCh.length - 1;
  let oldStartVnode = oldCh[0];
  let oldEndVnode = oldCh[oldEndIdx];
  let newEndIdx = newCh.length - 1;
  let newStartVnode = newCh[0];
  let newEndVnode = newCh[newEndIdx];
  let oldKeyToIdx, idxInOld, vnodeToMove, refElm;

  if (process.env.NODE_ENV !== "production") {
    checkDuplicateKeys(newCh);
  }

  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    if (isUndef(oldStartVnode)) {
      oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
    } else if (isUndef(oldEndVnode)) {
      oldEndVnode = oldCh[--oldEndIdx];
    } else if (sameVnode(oldStartVnode, newStartVnode)) {
      patchVnode(oldStartVnode, newStartVnode);
      oldStartVnode = oldCh[++oldStartIdx];
      newStartVnode = newCh[++newStartIdx];
    } else if (sameVnode(oldEndVnode, newEndVnode)) {
      patchVnode(oldEndVnode, newEndVnode);
      oldEndVnode = oldCh[--oldEndIdx];
      newEndVnode = newCh[--newEndIdx];
    } else if (sameVnode(oldStartVnode, newEndVnode)) {
      // Vnode moved right
      patchVnode(oldStartVnode, newEndVnode);

      oldStartVnode = oldCh[++oldStartIdx];
      newEndVnode = newCh[--newEndIdx];
    } else if (sameVnode(oldEndVnode, newStartVnode)) {
      // Vnode moved left
      patchVnode(oldEndVnode, newStartVnode);

      oldEndVnode = oldCh[--oldEndIdx];
      newStartVnode = newCh[++newStartIdx];
    } else {
      if (isUndef(oldKeyToIdx)) {
        oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
      }
      idxInOld = isDef(newStartVnode.key)
        ? oldKeyToIdx[newStartVnode.key]
        : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
      if (isUndef(idxInOld)) {
        // New element
        // createElm(
        //   newStartVnode,
        //   insertedVnodeQueue,
        //   parentElm,
        //   oldStartVnode.elm,
        //   false,
        //   newCh,
        //   newStartIdx
        // );
      } else {
        vnodeToMove = oldCh[idxInOld];
        if (sameVnode(vnodeToMove, newStartVnode)) {
          patchVnode(vnodeToMove, newStartVnode);
          oldCh[idxInOld] = undefined;
        } else {
          // same key but different element. treat as new element
          // createElm(
          //   newStartVnode,
          //   insertedVnodeQueue,
          //   parentElm,
          //   oldStartVnode.elm,
          //   false,
          //   newCh,
          //   newStartIdx
          // );
        }
      }
      newStartVnode = newCh[++newStartIdx];
    }
  }
  if (oldStartIdx > oldEndIdx) {
    refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
    // addVnodes(
    //   parentElm,
    //   refElm,
    //   newCh,
    //   newStartIdx,
    //   newEndIdx,
    //   insertedVnodeQueue
    // );
  } else if (newStartIdx > newEndIdx) {
    // removeVnodes(oldCh, oldStartIdx, oldEndIdx);
  }

  return newCh;
}

export { reconcile };
